#!/bin/bash
#
# $Id$
#
# Dump an SVN repository to a series of compressed files.  Uses a local file
# to remember the last changeset dumped so the output can be used for backups.
#

appname=`basename $0`

#
# Set some defaults
#
port="22"
increment=100
start=""
history_file="last_saved"
dumpfilepath="."
verbose=/bin/false
sshopts=""
identity=""

#
# Function to explain how to use the program
#
function usage () {
	echo
	echo "$appname [-h]"
	echo "$appname [-v] [-i increment] [-s start_rev] [--scp remote] [--identity ssh-key] [--ssh-opts ssh options] [--history-file file] [--out-dir directory name] [--compress (gzip|bzip2)] svn_repository_path"
	cat - <<EOF

This script dumps a subversion repository to a series of compressed
files.  It uses a local file to remember the last changeset dumped so
the script can be used to generate files for backups.

Using the --scp option, the backup files can be generated on one
machine and copied to a backup server once dumped.

  -v             -- Verbose mode
  -i increment   -- How many revisions to include in each dump file.
  -s start_rev   -- First revision to dump.
  --scp remote   -- Location for remote backups.  Files are transfered via scp,
                    then removed from the local directory.
  --identity     -- Identity file for scp transfer
  --ssh-opts      -- options to use for scp
  --history-file -- path and filename of historyfile to use
  --out-dir      -- path where svn dump files should be written, defaults to "."
  --compress     -- compression method (gzip or bzip2, defaults to bzip2)
EOF
	echo
	echo "Example:"
	echo "  $appname -v -i 100 --scp user@backupserver:/backups/svn /svn/Project"
	echo
	exit $1
}

compress_app="bzip2"
compress_ext="bzip2"

#
# Process arguments
#
while [ $# -gt 0 ]
do
    opt="$1"
	case "$opt" in
		-h) usage 0;;
		-i) increment=$2;
			shift;;
		-s) start=$2;
			shift;;
		--scp) dest="$2"; 
			shift;;
		--identity) identity="$2";
			shift;;
		--ssh-opts)  sshopts="$2";
			shift;;
		--history-file) history_file="$2";
			shift;;
		--out-dir) dumpfilepath="$2";
		    shift;;
		-v) verbose=/bin/true;;
		--compress)
		    case "$2" in
		        bzip2|bz|bzip) 
		            compress_app="bzip2"; 
		            compress_ext="bzip2";;
		        gzip|gz)
		            compress_app="gzip";
		            compress_ext="gz";;
	        esac;
	        shift;;
		*) break;;
	esac
	shift
done

repository="$1"
if [ -z "$repository" ]
then
	echo "Failed: Repository argument required"
	usage 1
fi

if [ "x${start}" = "x" ]
then
	if [ -s $history_file ]
	then
		loop_first=`cat $history_file`
	else
		loop_first=0
	fi
else
	loop_first=$start
fi
youngest=`svnlook youngest "$repository"`

$verbose && echo "Backing up: $repository"
$verbose && echo "      From: $loop_first"
$verbose && echo "        To: $youngest"
if [ "$dest" != "" ]
then
	$verbose && echo "      Dest: $dest"
fi
if [ "$identity" != "" ] ; then
	$verbose && echo "  Identity: $identity"
fi
if [ "$sshopts" != "" ] ; then
	$verbose && echo "  ssh opts: $sshopts"
fi
if [ "$history_file" != "" ] ; then
	$verbose && echo "Hist. file: $history_file"
fi
$verbose && echo "Chunk size: $increment"

#
# Function to do the backup for one set of revisions
#
function backup_revs () {
	typeset first=$1
	typeset last=$2
	typeset repo=$3

	if [ "$first" != "0" ]
	then
		incremental="--incremental"
	fi

	repo_name=`basename "$repo"`
	dumpfile="$dumpfilepath/dumpfile-${repo_name}-${first}-${last}.${compress_ext}"

	$verbose && echo -n "Dumping ${first}:${last} ..."

    svnadmin dump -q "$repo" $incremental --revision ${first}:${last} \
        | $compress_app > $dumpfile
	RC=$?

	$verbose && echo

	if [ $RC -ne 0 ]
	then
		rm -f $dumpfile
		return $RC
	fi

	if [ "$dest" != "" ]
	then
		destfile=${dest}/$dumpfile
		if [ -z "$identity" ] ; then
			scp $sshopts $dumpfile $destfile
		else
			scp $sshopts -i $identity $dumpfile $destfile
		fi
		RC=$?
		rm -f $dumpfile
	fi

	return $RC
}

#
# Do the incremental dumps
#

if [[ $youngest -eq $loop_first ]]
then
	$verbose && echo "No new changesets to dump"
	exit 0
fi

let loop_last=($loop_first + $increment)

if [[ $loop_first -ne 0 ]]
then
	let loop_first=($loop_first + 1)
fi

while [[ $loop_first -le $youngest ]]
do

	if [ $loop_last -lt $youngest ]
	then
		# A full "increment"
		backup_revs $loop_first $loop_last "$repository" || exit 1
		echo $loop_last > $history_file
	else
		# In case the last few revs do not make up a full "increment"
		backup_revs $loop_first $youngest "$repository" || exit 1
		echo $youngest > $history_file
	fi

	let loop_first=($loop_last + 1)
	let loop_last=($loop_last + $increment)
done

exit 0
